use std::iter::empty;
use std::ptr::null_mut;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::cell::RefCell;
use std::rc::Rc;
use std::mem;
use std::os::raw::{c_long,c_ulong};
use crate::params::MI_MAX_ALIGN_SIZE;
use super::*;

pub const MI_SECURE: u8 = 0; 

pub const MI_DEBUG: u8 = {
    #[cfg(debug_assertions)]
    {2} 
    #[cfg(not(debug_assertions))]
    {0}
};

pub const MI_PADDING: bool = MI_SECURE >= 3 || MI_DEBUG >= 1 || cfg!(any(MI_TRACK_VALGRIND, MI_TRACK_ASAN, MI_TRACK_ETW));

pub const MI_PADDING_CHECK: bool = MI_PADDING && (MI_SECURE >= 3 || MI_DEBUG >= 1);

pub const MI_ENCODE_FREELIST: bool = MI_SECURE >= 3 || MI_DEBUG >= 1;

// judge pointer size 
pub const MI_INTPTR_SHIFT: u8 = if mem::size_of::<usize>() == 16 { 4 }
    else if mem::size_of::<usize>() == 8 { 3 } 
    else if mem::size_of::<usize>() == 4 { 2 }
    else {
        panic!("Platform pointers must be 32, 64, or 128 bits");
    };

// judge object size 
pub const MI_SIZE_SHIFT: u8 = if mem::size_of::<usize>() == 8 {
    3 // 64-bit size
} else if mem::size_of::<usize>() == 4 {
    2 // 32-bit size
} else {
    panic!("Platform objects must be 32 or 64 bits");
};

// type definitions
#[cfg(target_pointer_width = "64")]
pub type MiSsizeT = i64;

#[cfg(target_pointer_width = "32")]
pub type MiSsizeT = i32;

pub const fn mi_zu(x: u64) -> u64 {
    if usize::MAX / 2 > c_ulong::MAX as usize {
        x // Use `ULL` style (implicitly handled in Rust)
    } else {
        x // Use `UL` style (same in Rust)
    }
}

pub const fn mi_zi(x: i64) -> i64 {
    if usize::MAX / 2 > c_long::MAX as usize {
        x // Use `LL` style
    } else {
        x // Use `L` style
    }
}

pub const MI_INTPTR_SIZE: usize = 1 << MI_INTPTR_SHIFT;
pub const MI_INTPTR_BITS: usize = MI_INTPTR_SIZE * 8;

pub const MI_SIZE_SIZE: usize = 1 << MI_SIZE_SHIFT;
pub const MI_SIZE_BITS: usize = MI_SIZE_SIZE * 8;

pub const MI_KiB: u64 = mi_zu(1024);
pub const MI_MiB: u64 = MI_KiB * MI_KiB;
pub const MI_GiB: u64 = MI_MiB * MI_KiB;

pub const MI_SEGMENT_SLICE_SHIFT: usize = (13 + MI_INTPTR_SHIFT) as usize; // 64KiB (32KiB on 32-bit)

pub const MI_SEGMENT_SHIFT: usize = if MI_INTPTR_SIZE > 4 {
    9 + MI_SEGMENT_SLICE_SHIFT // 32MiB
} else {
    7 + MI_SEGMENT_SLICE_SHIFT // 4MiB on 32-bit
};

pub const MI_SMALL_PAGE_SHIFT: usize = MI_SEGMENT_SLICE_SHIFT; // 64KiB
pub const MI_MEDIUM_PAGE_SHIFT: usize = 3 + MI_SMALL_PAGE_SHIFT; // 512KiB

pub const MI_SEGMENT_SIZE: usize = 1 << MI_SEGMENT_SHIFT;
pub const MI_SEGMENT_ALIGN: usize = MI_SEGMENT_SIZE;
pub const MI_SEGMENT_MASK: usize = MI_SEGMENT_ALIGN - 1;
pub const MI_SEGMENT_SLICE_SIZE: usize = 1 << MI_SEGMENT_SLICE_SHIFT;
pub const MI_SLICES_PER_SEGMENT: usize = MI_SEGMENT_SIZE / MI_SEGMENT_SLICE_SIZE; // 1024

pub const MI_SMALL_PAGE_SIZE: usize = 1 << MI_SMALL_PAGE_SHIFT;
pub const MI_MEDIUM_PAGE_SIZE: usize = 1 << MI_MEDIUM_PAGE_SHIFT;

pub const MI_SMALL_OBJ_SIZE_MAX: usize = MI_SMALL_PAGE_SIZE / 4; // 8KiB on 64-bit
pub const MI_MEDIUM_OBJ_SIZE_MAX: usize = MI_MEDIUM_PAGE_SIZE / 4; // 128KiB on 64-bit
pub const MI_MEDIUM_OBJ_WSIZE_MAX: usize = MI_MEDIUM_OBJ_SIZE_MAX / MI_INTPTR_SIZE;
pub const MI_LARGE_OBJ_SIZE_MAX: usize = MI_SEGMENT_SIZE / 2; // 32MiB on 64-bit
pub const MI_LARGE_OBJ_WSIZE_MAX: usize = MI_LARGE_OBJ_SIZE_MAX / MI_INTPTR_SIZE;

// Maximum number of size classes. (spaced exponentially in 12.5% increments)
pub const MI_BIN_HUGE: u32 = 73;

const _: () = {
    assert!(MI_MEDIUM_OBJ_WSIZE_MAX < 655360, "mimalloc internal: define more bins");
};


// Maximum block size for which blocks are guaranteed to be block size aligned.
pub const MI_MAX_ALIGN_GUARANTEE: usize = MI_MEDIUM_OBJ_SIZE_MAX;

// Alignments over MI_BLOCK_ALIGNMENT_MAX are allocated in dedicated huge page segments
pub const MI_BLOCK_ALIGNMENT_MAX: usize = MI_SEGMENT_SIZE >> 1;

// Maximum slice count for which we can find the page for interior pointers
pub const MI_MAX_SLICE_OFFSET_COUNT: usize = (MI_BLOCK_ALIGNMENT_MAX / MI_SEGMENT_SLICE_SIZE) - 1;

// We never allocate more than PTRDIFF_MAX
pub const MI_MAX_ALLOC_SIZE: usize = if std::isize::MAX > std::i32::MAX as isize && std::isize::MAX >= (MI_SEGMENT_SLICE_SIZE as isize * std::u32::MAX as isize) {
    MI_SEGMENT_SLICE_SIZE as usize * (std::u32::MAX as usize - 1)
} else {
    std::isize::MAX as usize
};


pub type MiEncoded = usize; 

pub type MiThreadId = usize; 

#[repr(C)]
pub struct MiBlock {
    pub next: MiEncoded,
}


#[derive(Debug, Clone, Copy,PartialEq)]
pub enum MiDelayed {
    MI_USE_DELAYED_FREE = 0,
    MI_DELAYED_FREEING = 1,
    MI_NO_DELAYED_FREE = 2,
    MI_NEVER_DELAYED_FREE = 3,
}

#[cfg(not(feature = "MI_TSAN"))]
#[repr(C)]
#[derive(Clone, Copy)]
pub union MiPageFlags {
    pub full_aligned: u8,
    pub x: MiPageFlagsInner,
}

#[cfg(not(feature = "MI_TSAN"))]
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct MiPageFlagsInner {
    pub in_full: u8,
    pub has_aligned: u8, 
}

#[cfg(feature = "MI_TSAN")]
#[repr(C)]
pub union MiPageFlags {
    pub full_aligned: u16,
    pub x: MiPageFlagsTSANInner,
}

#[cfg(feature = "MI_TSAN")]
#[repr(C)]
pub struct MiPageFlagsTSANInner {
    pub in_full: u8,
    pub has_aligned: u8,
}

pub type MiThreadFree = usize;

#[repr(C)]
pub struct MiPage {
    pub slice_count: u32,
    pub slice_offset: u32,
    pub is_committed: u8,
    pub is_zero_init: u8,
    pub is_huge: u8,
    pub capacity: u16,
    pub reserved: u16,
    pub flags: MiPageFlags,
    pub free_is_zero: u8,
    pub retire_expire: u8,
    pub free: *mut MiBlock,                  
    pub local_free: *mut MiBlock,           
    pub used: u16,
    pub block_size_shift: u8,
    pub heap_tag: u8,
    pub block_size: usize,
    pub page_start: *mut u8,

    //#[cfg(any(feature = "MI_ENCODE_FREELIST", feature = "MI_PADDING"))]
    //pub keys: [usize; 2],
    
    pub keys: [usize; 2],
    pub xthread_free: AtomicUsize,
    pub xheap: AtomicUsize,               
    pub next: Option<Rc<RefCell<MiPage>>>,        
    pub prev: Option<Rc<RefCell<MiPage>>>,   
    pub padding: [*mut std::ffi::c_void; 1],
}

impl MiPage {
    pub fn to_option_rc_refcell(self) -> Option<Rc<RefCell<types::MiPage>>> {
        Some(Rc::new(RefCell::new(self))) 
    }
}

impl Clone for MiPage {
    fn clone(&self) -> Self {
        MiPage {
            slice_count: self.slice_count,
            slice_offset: self.slice_offset,
            is_committed: self.is_committed,
            is_zero_init: self.is_zero_init,
            is_huge: self.is_huge,
            capacity: self.capacity,
            reserved: self.reserved,
            flags: self.flags.clone(),
            free_is_zero: self.free_is_zero,
            retire_expire: self.retire_expire,
            free: self.free, 
            local_free: self.local_free, 
            used: self.used,
            block_size_shift: self.block_size_shift,
            heap_tag: self.heap_tag,
            block_size: self.block_size,
            page_start: self.page_start, 
            keys: self.keys, 
            xthread_free: AtomicUsize::new(self.xthread_free.load(std::sync::atomic::Ordering::SeqCst)),
            xheap: AtomicUsize::new(self.xheap.load(std::sync::atomic::Ordering::SeqCst)),
            next: self.next.clone(), 
            prev: self.prev.clone(), 
            padding: self.padding, 
        }
    }
}


#[derive(Debug, Clone, Copy)]
pub enum MiPageKind {
    MI_PAGE_SMALL,
    MI_PAGE_MEDIUM,
    MI_PAGE_LARGE,
    MI_PAGE_HUGE,
}

#[derive(Debug, Clone, Copy,PartialEq)]
pub enum MiSegmentKind {
    MI_SEGMENT_NORMAL,
    MI_SEGMENT_HUGE,
}

pub const MI_MINIMAL_COMMIT_SIZE: usize = 1 * MI_SEGMENT_SLICE_SIZE;
pub const MI_COMMIT_SIZE: usize = MI_SEGMENT_SLICE_SIZE; // 64KiB
pub const MI_COMMIT_MASK_BITS: usize = MI_SEGMENT_SIZE / MI_COMMIT_SIZE;
pub const MI_COMMIT_MASK_FIELD_BITS: usize = MI_SIZE_BITS;
pub const MI_COMMIT_MASK_FIELD_COUNT: usize = MI_COMMIT_MASK_BITS / MI_COMMIT_MASK_FIELD_BITS;

const _: () = {assert!(
    MI_COMMIT_MASK_BITS == (MI_COMMIT_MASK_FIELD_COUNT * MI_COMMIT_MASK_FIELD_BITS),
        "the segment size must be exactly divisible by the (commit size * size_t bits)"
    );
};

#[repr(C)]
pub struct MiCommitMask {
    pub mask: [usize; MI_COMMIT_MASK_FIELD_COUNT],
}

pub type MiSlice = MiPage;
pub type MiMsecs = i64;

#[derive(Debug, Clone, Copy)]
pub enum MiMemKind {
    MI_MEM_NONE,
    MI_MEM_EXTERNAL,
    MI_MEM_STATIC,
    MI_MEM_OS,
    MI_MEM_OS_HUGE,
    MI_MEM_OS_REMAP,
    MI_MEM_ARENA,
}

pub fn mi_memkind_is_os(memkind: MiMemKind) -> bool {
    (memkind as u8) >= MiMemKind::MI_MEM_OS as u8 && (memkind as u8) <= MiMemKind::MI_MEM_OS_REMAP as u8
}

#[repr(C)]
pub struct MiMemIdOsInfo {
    pub base: *mut std::ffi::c_void,
    pub alignment: usize,
}

#[repr(C)]
pub struct MiMemIdArenaInfo {
    pub block_index: usize,
    pub id: MiArenaId, 
    pub is_exclusive: bool,
}

#[repr(C)]
pub struct MiMemId {
    pub mem: MiMemIdUnion,
    pub is_pinned: bool,
    pub initially_committed: bool,
    pub initially_zero: bool,
    pub memkind: MiMemKind, 
}

impl MiMemId {
    pub const fn new_empty() -> Self {
        MiMemId {
            mem: MiMemIdUnion { os: std::mem::ManuallyDrop::new(MiMemIdOsInfo { base: null_mut(), alignment: 0 }) },
            is_pinned: false,
            initially_committed: false,
            initially_zero: false,
            memkind: MiMemKind::MI_MEM_NONE,
        }
    }
}

#[repr(C)]
pub union MiMemIdUnion {
    pub os: std::mem::ManuallyDrop<MiMemIdOsInfo>,        
    pub arena: std::mem::ManuallyDrop<MiMemIdArenaInfo>, 
}


#[repr(C)]
pub struct MiSegment {
    pub memid: MiMemId,
    pub allow_decommit: bool,
    pub allow_purge: bool,
    pub segment_size: usize,

    pub purge_expire: MiMsecs,
    pub purge_mask: MiCommitMask,
    pub commit_mask: MiCommitMask,

    pub next: *mut MiSegment,
    pub was_reclaimed: bool,

    pub abandoned: usize,
    pub abandoned_visits: usize,
    pub used: usize,
    pub cookie: usize,

    pub segment_slices: usize,
    pub segment_info_slices: usize,

    pub kind: MiSegmentKind,
    pub slice_entries: usize,
    pub thread_id: AtomicUsize,

    pub slices: [MiSlice; MI_SLICES_PER_SEGMENT + 1],
}

#[repr(C)]
pub struct MiTld {
    pub heartbeat: u64,
    pub recurse: bool,
    pub heap_backings: *mut MiHeap,
    pub heaps: *mut MiHeap,
    pub segments: MiSegmentsTld,
    pub os: MiOsTld,
    pub stats: MiStats,
}

#[repr(C)]
pub struct MiPageQueue {
    pub first: Option<Rc<RefCell<MiPage>>>,
    pub last:Option<Rc<RefCell<MiPage>>>,
    pub block_size: usize,
}

impl Clone for MiPageQueue {
    fn clone(&self) -> Self {
        MiPageQueue {
            first: self.first.clone(),
            last: self.last.clone(),
            block_size: self.block_size,
        }
    }
}

impl MiPageQueue {
    pub fn to_option_rc_refcell(&self) -> Option<Rc<RefCell<MiPageQueue>>> {
        Some(Rc::new(RefCell::new(self.clone()))) 
    }
}
pub trait ToOptionRcRefCell {
    fn to_option_rc_refcell(self) -> Option<Rc<RefCell<MiPageQueue>>>;
}

impl ToOptionRcRefCell for *mut MiPageQueue {
    fn to_option_rc_refcell(self) -> Option<Rc<RefCell<MiPageQueue>>> {
        if self.is_null() {
            None
        } else {
            unsafe {
                let page_queue: &MiPageQueue = &*self; 
                Some(Rc::new(RefCell::new(page_queue.clone()))) 
            }
        }
    }
}

impl MiPageQueue {
    pub const fn empty_new() -> Self {
        MiPageQueue {
            first: None,
            last: None,
            block_size: 0,
        }
    }

    pub const fn empty_pointer() -> *mut Self {
        let queue = MiPageQueue {
            first: None,
            last: None,
            block_size: 0,
        };
        &queue as *const _ as *mut Self
    }

}
pub const MI_BIN_FULL: usize = (MI_BIN_HUGE + 1) as usize;

#[repr(C)]
#[derive(Clone, Copy)]
pub struct MiRandomCtx {
    pub input: [u32; 16],
    pub output: [u32; 16],
    pub output_available: i32,
    pub weak: bool,
}

#[cfg(MI_PADDING) ]
#[repr(C)]
pub struct MiPadding {
    pub canary: u32,
    pub delta: u32,
}

#[cfg(MI_PADDING) ]
pub const MI_PADDING_SIZE: usize = std::mem::size_of::<MiPadding>();
#[cfg(MI_PADDING) ]
pub const MI_PADDING_WSIZE: usize = (MI_PADDING_SIZE + MI_INTPTR_SIZE - 1) / MI_INTPTR_SIZE;

#[cfg(not(MI_PADDING)) ]
pub const MI_PADDING_SIZE: usize = 0;

#[cfg(not(MI_PADDING)) ]
pub const MI_PADDING_WSIZE: usize = 0;

pub const MI_PAGES_DIRECT: usize = MI_SMALL_WSIZE_MAX + MI_PADDING_WSIZE + 1;

#[repr(C)]
pub struct MiHeap {
    pub tld: *mut MiTld,
    pub thread_delayed_free: AtomicUsize, 
    pub thread_id: AtomicUsize,
    pub arena_id: MiArenaId,                  
    pub cookie: usize,
    pub keys: [usize; 2],
    pub random: MiRandomCtx,
    pub page_count: usize,
    pub page_retired_min: usize,
    pub page_retired_max: usize,
    pub next: *mut MiHeap,            //todo: change bare pointers
    pub no_reclaim: bool,
    pub tag: u8,
    pub pages_free_direct: [*mut MiPage; MI_PAGES_DIRECT], //todo: change bare pointers
    pub pages: [* mut MiPageQueue; MI_BIN_FULL + 1],
}

impl MiHeap {
    pub fn as_mut_ptr(&mut self) -> *mut Self {
        self as *mut Self
    }

    pub const  fn new_empty() -> Self {
        MiHeap {
            tld: null_mut(),
            thread_delayed_free: AtomicUsize::new(0),
            thread_id: AtomicUsize::new(0),
            arena_id: 0, 
            cookie: 0,   
            keys: [0; 2],
            random: MiRandomCtx {
                input: [0; 16],
                output: [0; 16],
                output_available: 0,
                weak: false,
            },
            page_count: 0,
            page_retired_min: 0,
            page_retired_max: 0,
            next: null_mut(),
            no_reclaim: false,
            tag: 0,
            pages_free_direct: [null_mut(); MI_PAGES_DIRECT],
            pages: [MiPageQueue::empty_pointer(); MI_BIN_FULL + 1],

        }
    }
}

use std::ptr;

impl Clone for MiHeap {
    fn clone(&self) -> Self {
        MiHeap {
            tld: self.tld, 
            thread_delayed_free: AtomicUsize::new(self.thread_delayed_free.load(std::sync::atomic::Ordering::SeqCst)),
            thread_id: AtomicUsize::new(self.thread_id.load(std::sync::atomic::Ordering::SeqCst)),
            arena_id: self.arena_id, 
            cookie: self.cookie,
            keys: self.keys,
            random: self.random, 
            page_count: self.page_count,
            page_retired_min: self.page_retired_min,
            page_retired_max: self.page_retired_max,
            next: self.next, 
            no_reclaim: self.no_reclaim,
            tag: self.tag,
            pages_free_direct: self.pages_free_direct, 
            pages: self.pages, 
        }
    }
}


/// debug part remaining here

pub const MI_DEBUG_UNINIT: u8 = 0xD0;
pub const MI_DEBUG_FREED: u8 = 0xDF; 
pub const MI_DEBUG_PADDING: u8 = 0xDE;

#[cfg(debug_assertions)]
pub fn mi_assert_fail(assertion: &str, fname: &str, line: u32, func: &str) {
    eprintln!("Assertion failed: {}\nFile: {}\nLine: {}\nFunction: {}", assertion, fname, line, func);
    panic!();
}

#[macro_export]
macro_rules! mi_assert {
    ($expr:expr) => {
        if !$expr {
            mi_assert_fail(stringify!($expr), file!(), line!() as u32, std::any::type_name::<()>());
        }
    };
}
pub const MI_DEBUG_GTONE:  bool = MI_DEBUG > 1;
pub const MI_DEBUG_GTTWO:  bool = MI_DEBUG > 2;

#[cfg(MI_DEBUG_GTTWO)]
#[macro_export]
macro_rules! mi_assert_expensive {
    ($x:expr) => {
        mi_assert!($x);
    };
}

pub const MI_STAT: u8 = {
    if MI_DEBUG > 0{
        2
    }
    else{
        0
    }
};

#[repr(C)]
pub struct MiStatCount {
    pub allocated: i64,
    pub freed: i64,
    pub peak: i64,
    pub current: i64,
}

#[repr(C)]
pub struct MiStatCounter {
    pub total: i64,
    pub count: i64,
}

pub const MI_STAT_GTONE: bool = MI_STAT > 1;
#[repr(C)]
pub struct MiStats {
    pub segments: MiStatCount,
    pub pages: MiStatCount,
    pub reserved: MiStatCount,
    pub committed: MiStatCount,
    pub reset: MiStatCount,
    pub purged: MiStatCount,
    pub page_committed: MiStatCount,
    pub segments_abandoned: MiStatCount,
    pub pages_abandoned: MiStatCount,
    pub threads: MiStatCount,
    pub normal: MiStatCount,
    pub huge: MiStatCount,
    pub large: MiStatCount,
    pub malloc: MiStatCount,
    pub segments_cache: MiStatCount,
    pub pages_extended: MiStatCounter,
    pub mmap_calls: MiStatCounter,
    pub commit_calls: MiStatCounter,
    pub reset_calls: MiStatCounter,
    pub purge_calls: MiStatCounter,
    pub page_no_retire: MiStatCounter,
    pub searches: MiStatCounter,
    pub normal_count: MiStatCounter,
    pub huge_count: MiStatCounter,
    pub large_count: MiStatCounter,
    pub arena_count: MiStatCounter,
    pub arena_crossover_count: MiStatCounter,
    pub arena_rollback_count: MiStatCounter,
    
    #[cfg(MI_STAT_GTONE)] 
    pub normal_bins: [MiStatCount; MI_BIN_HUGE + 1], 
}

//stat.rs
#[cfg(MI_STAT)]
#[macro_export]
macro_rules! mi_stat_increase {
    ($stat:expr, $amount:expr) => {
        mi_stat_increase(&$stat, $amount)
    };
}

#[cfg(MI_STAT)]
#[macro_export]
macro_rules! mi_stat_decrease {
    ($stat:expr, $amount:expr) => {
        mi_stat_decrease(&$stat, $amount)
    };
}

#[cfg(MI_STAT)]
#[macro_export]
macro_rules! mi_stat_counter_increase {
    ($stat:expr, $amount:expr) => {
        mi_stat_counter_increase(&$stat, $amount)
    };
}

#[cfg(not(MI_STAT))]
#[macro_export]
macro_rules! mi_stat_increase {
    ($stat:expr, $amount:expr) => {};
}

#[cfg(not(MI_STAT))]
#[macro_export]
macro_rules! mi_stat_decrease {
    ($stat:expr, $amount:expr) => {};
}

#[cfg(not(MI_STAT))]
#[macro_export]
macro_rules! mi_stat_counter_increase {
    ($stat:expr, $amount:expr) => {};
}

#[macro_export]
macro_rules! mi_heap_stat_counter_increase {
    ($heap:expr, $stat:ident, $amount:expr) => {
        mi_stat_counter_increase!(((*$heap).tld.stats.$stat), $amount)
    };
}

#[macro_export]
macro_rules! mi_heap_stat_increase {
    ($heap:expr, $stat:ident, $amount:expr) => {
        mi_stat_increase!(((*$heap).tld.stats.$stat), $amount)
    };
}

#[macro_export]
macro_rules! mi_heap_stat_decrease {
    ($heap:expr, $stat:ident, $amount:expr) => {
        mi_stat_decrease!(((*$heap).tld.stats.$stat), $amount)
    };
}

// ------------------------------------------------------
// Thread Local data
// ------------------------------------------------------

#[repr(C)]
pub struct MiSpanQueue{
    first: *mut MiSlice,
    last: *mut MiSlice,
    slice_count: usize,
}

pub const MI_SEGMENT_BIN_MAX: usize = 35;

#[repr(C)]
pub struct MiOsTld {
    pub region_idx: usize,  
    pub stats: *mut MiStats, 
}

#[repr(C)]
pub struct MiSegmentsTld {
    pub spans: [MiSpanQueue; MI_SEGMENT_BIN_MAX + 1], 
    pub count: usize,            
    pub peak_count: usize,        
    pub current_size: usize,      
    pub peak_size: usize,         
    pub reclaim_count: usize,     
    pub stats: *mut MiStats,   // todo: change bare pointers      
    pub os: *mut MiOsTld,      //todo: change bare pointers   
}
